package cn.maihe.elg.operation.supports.electronicseal.jd.service;

import cn.maihe.elg.operation.supports.electronicseal.jd.config.JDSignatureConfig;
import cn.maihe.elg.operation.supports.electronicseal.jd.dto.req.DownloadSignatureFileReqDTO;
import cn.maihe.elg.operation.supports.electronicseal.jd.dto.req.FileSignatureReqDTO;
import cn.maihe.elg.operation.supports.electronicseal.jd.dto.req.GenerateSignatureFileReqDTO;
import cn.maihe.elg.operation.supports.electronicseal.jd.dto.req.UploadAssembleOfdFileReqDTO;
import cn.maihe.elg.operation.supports.electronicseal.jd.dto.req.UploadPendingSignatureFileReqDTO;
import cn.maihe.elg.operation.supports.electronicseal.jd.dto.resp.FileSignatureRespDTO;
import cn.maihe.elg.operation.supports.electronicseal.jd.dto.resp.GenerateSignatureFileRespDTO;
import cn.maihe.elg.operation.supports.electronicseal.jd.dto.resp.JDOfdSignatureBaseDTO;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.util.Base64;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.stream.Collectors;

/**
 * @Description 佳盾签章接口
 * @Author SSX
 * @Date 2025/07/09
 */
@Slf4j
public abstract class JDSignatureService {
    protected abstract JDOfdSignatureBaseDTO uploadPendingSignatureFile(String url, UploadPendingSignatureFileReqDTO reqDTO, MultiValueMap<String, Object> body);
    protected abstract JDOfdSignatureBaseDTO uploadAssembleOfdFile(String url,UploadAssembleOfdFileReqDTO reqDTO, MultiValueMap<String, Object> body);
    protected abstract FileSignatureRespDTO fileSignature(String url,FileSignatureReqDTO reqDTO, MultiValueMap<String, Object> body);
    protected abstract GenerateSignatureFileRespDTO generateSignatureFile(String url,GenerateSignatureFileReqDTO reqDTO, MultiValueMap<String, Object> body);
    protected abstract Path downloadSignatureFile(String url,DownloadSignatureFileReqDTO reqDTO,String savePath, MultiValueMap<String, Object> body);

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private JDSignatureConfig jdSignatureConfig;

    private final ObjectMapper objectMapper = new ObjectMapper();

    protected String buildMessageDigest(Object sourceBody, String [] excludeKeys) {
        List<String> excludeKeyList = (Objects.isNull(excludeKeys) || excludeKeys.length == 0) ? null : Arrays.asList(excludeKeys);

        TreeMap<String, Object> treeMap = objectMapper.convertValue(sourceBody, TreeMap.class);
        String summaryMsg = treeMap.entrySet().stream()
                .filter(entry -> CollectionUtils.isEmpty(excludeKeyList) || !excludeKeyList.contains(entry.getKey()))
                .filter(entry -> Objects.nonNull(entry.getValue()) && StringUtils.isNotBlank(String.valueOf(entry.getValue())))
                .map(entry -> entry.getKey() +"="+ entry.getValue()).collect(Collectors.joining("&"));
        return summaryMsg;
    }

    protected String generateToken(Object body,String appId,String key,long timestamp,String [] excludeKeys) {
        String summaryMsg = this.buildMessageDigest(body, excludeKeys);
        String hash = DigestUtils.md5Hex(summaryMsg).toUpperCase();
        String stringTemp = String.format("appId=%s&code=%s&timestamp=%s&key=%s", appId, hash,timestamp,key);
        String sign = DigestUtils.md5Hex(stringTemp).toUpperCase();
        JSONObject authObj = new JSONObject();
        authObj.put("appId", appId);
        authObj.put("code", hash);
        authObj.put("timestamp", timestamp);
        authObj.put("sign", sign);
        return new String(Base64.encodeBase64(authObj.toJSONString().getBytes(StandardCharsets.UTF_8)));
    }

    public <RESP, REQ> RESP doRequest(String url,REQ req,MultiValueMap<String, Object> body,Class<RESP> respClazz) {
        Map<String, String> headers = new TreeMap<>();
        String token = generateToken(req, jdSignatureConfig.getAppId(), jdSignatureConfig.getKey(), System.currentTimeMillis(),null);
        headers.put("JD-SERVICE-AUTH", token);

        HttpHeaders httpHeaders = new HttpHeaders();
        headers.forEach(httpHeaders::set);
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        try {
            HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(body, httpHeaders);
            ResponseEntity<RESP> responseEntity = restTemplate.postForEntity(url, request, respClazz);
            if (responseEntity != null && responseEntity.getStatusCode().is2xxSuccessful()) {
                //正常响应业务处理
                return responseEntity.getBody();
            }

        } catch (Exception e) {
            log.error("签章接口调用异常"+e.getMessage(),e);
        }
        return null;
    }

    public <RESP, REQ> RESP doRequestFormFata(String url,REQ req, MultiValueMap<String, Object> body,Class<RESP> respClazz) {
        String token = generateToken(req, jdSignatureConfig.getAppId(), jdSignatureConfig.getKey(), System.currentTimeMillis(),null);
        Map<String, String> headers = new TreeMap<>();
        headers.put("JD-SERVICE-AUTH", token);
        HttpHeaders httpHeaders = new HttpHeaders();
        headers.forEach(httpHeaders::set);
        httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
        try {
            HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(body, httpHeaders);
            ResponseEntity<RESP> responseEntity = restTemplate.postForEntity(url, request, respClazz);
            if (responseEntity != null && responseEntity.getStatusCode().is2xxSuccessful()) {
                //正常响应业务处理
                return responseEntity.getBody();
            }
        } catch (Exception e) {
            log.error("待签署文件接口调用异常"+e.getMessage(),e);
        }
        return null;
    }

    public <REQ> Path downloadEnterpriseFile(String url,REQ req, MultiValueMap<String, Object> body,String savePath) {
        Map<String, String> headers = new TreeMap<>();
        String token = generateToken(req, jdSignatureConfig.getAppId(), jdSignatureConfig.getKey(), System.currentTimeMillis(), null);
        headers.put("JD-SERVICE-AUTH", token);

        HttpHeaders httpHeaders = new HttpHeaders();
        headers.forEach(httpHeaders::set);
        httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
        HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(body, httpHeaders);
        try {

            ResponseEntity<byte[]> response = restTemplate.exchange(
                    url,
                    HttpMethod.POST,
                    request,
                    byte[].class
            );

            Path outputFile = Paths.get(savePath);
            try (InputStream inputStream = new ByteArrayInputStream(response.getBody())) {
                Files.copy(
                        inputStream,
                        outputFile,
                        StandardCopyOption.REPLACE_EXISTING
                );

                // 验证文件完整性
                if (Files.size(outputFile) == 0) {
                    Files.deleteIfExists(outputFile);
                    log.error("下载文件为空");
                    throw new RuntimeException("下载文件为空");
                }
            } catch (IOException e) {
                log.error("文件保存失败: "+e.getMessage(),e);
                throw new RuntimeException("文件保存失败: " + e.getMessage(), e);
            }
            return outputFile;
        } catch (Exception e) {
            log.error("文件下载异常: "+e.getMessage(),e);
        }
        return null;
   }
}
